#ifndef __IO_MANAGER__
#define __IO_MANAGER__

#include "fiber.h"
#include "scheduler.h"
#include "timer.h"

#include <atomic>
#include <cstdint>
#include <functional>
#include <shared_mutex>

class IOManager:public Scheduler, public TimerManager
{
public:
    using ptr = std::shared_ptr<IOManager>;

    enum Event
    {
        NONE    = 0x0,
        READ    = 0x1,  //读事件（EPOLLIN）
        WRITE   = 0x4   //写事件（EPOLLOUT）
    };

    //线程池默认线程数量
    //是否使用当前调用线程(是否将调用线程包含进去)
    //协程调度器名称
    IOManager(size_t threads = 1, bool use_caller = true, const std::string& name = "");
    ~IOManager();

    //给对应fd添加对应类型事件
    //return 成功返回0，失败返回-1
    int addEvent(int fd, Event event, std::function<void()> cb = nullptr);

    //删除事件（不会触发事件）
    bool delEvent(int fd, Event event);

    //取消事件（如果事件存在则触发事件）
    bool cancelEvent(int fd, Event event);

    //取消对应fd上所有事件
    bool cancelAll(int fd);

    //返回 协程调度器的指针
    static IOManager* GetThis();
protected:
    //通知协程调度器有任务了
    void tickle() override;

    //协程无任务时调用idle协程
    void idle() override;

    //返回是否可以停止
    bool stopping() override;

    //当有新的定任务插入到定队列的首部，执行该函数
    void onTimerInsertedAtFront() override;

private:
    //判断是否可以停止
    //timeout 最近要触发的定时器事件时间间隔
    bool stopping(uint64_t& timeout);

    //重置m_fdContexts容器大小
    void contextResize(size_t size);
    
private:
    //事件类 - 对应每个fd上的所有事件
    struct FdContext
    {
        //具体的事件任务
        struct EventContext
        {
            Scheduler* scheduler = nullptr; //该任务在那个调度器上执行
            //任务可以是协程，也可以是回调函数
            Fiber::ptr fiberEvent;
            std::function<void()> cbEvent;
        };

        //重置事件
        void resetContext(EventContext& ctx);

        //根据事件类型获取对应事件
        EventContext& getContext(Event event);

        //触发事件 - 将事件添加到调度器中
        void triggerEvent(Event event);

        int fd = 0;             //文件描述符
        Event events = NONE;    //当前文件描述符所有事件类型
        EventContext readEvent; //读事件
        EventContext writeEvent;//写事件
        std::mutex fdMutex;     //fd锁
    };

    int m_epfd = 0;     //epoll 红黑树
    int m_tickleFds[2]; //pipe 文件句柄，[0]表示管道的读端,[1] 表示管道的写端。
    //C++17读写锁 std::shared_lock 用于读取锁定，std::unique_lock 用于写入锁定
    std::shared_mutex       m_sharedMutex;       //读写锁
    std::atomic<size_t>     m_pendingEventCount; //当前等待执行事件的数量
    std::vector<FdContext*> m_fdContexts;        //fd容器，容器下标对应于相应的fd      
};

#endif